#pragma once

#include <iostream>
#include <string>
#include <cassert>

struct DecoderTest {
	static std::string decode (const std::string& what, const std::string & check) {
		std::string const chars{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,? "};
		std::string res; res.reserve(what.size());
		for( size_t pos = 0; pos < what.size(); ++pos ){
			if( chars.find( what[pos] ) != std::string::npos ){
				size_t idx = chars.find(what[pos]) + 1;
				for( size_t i = 0; i <= pos; ++i )
				{
					while( idx % 2 == 1 || idx == 0 ){
						idx += 67;
					}
					idx /= 2;
				}
				assert(check[pos] == chars[idx-1]);

				std::cout << "tr: " << what[pos] << "  " << chars[idx-1] << " count = " << 0 << std::endl;

				res.push_back(chars[idx-1]);
			} else {
				res.push_back(what[pos]);
			}
		}
		return res;
	}

	static std::string encode (std::string const & what, std::string const & check){
		std::string const chars{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,? "};
		for( auto i = 0; i < chars.size() ; ++ i ){
			std::cout << i + 1 << " = " << chars[i] << std::endl;
		}

		std::string enc;
		for( size_t pos = 0; pos < what.size(); ++pos ){
			if( chars.find( what[pos] ) != std::string::npos ){
				uint64_t idx = chars.find(what[pos]) + 1;

				//uint64_t idx =  ( ( (uint64_t)2 << pos ) * idx  ) % ( chars.size() + 1 );

				uint64_t newIdx = 2 * idx;
				for( auto i = 0; i < pos; ++i ){
					newIdx *= 2;
					while(newIdx > 67 )
						newIdx -= 67;
				}
					while(newIdx > 67 )
						newIdx -= 67;

				//assert(newIdx == idx);

				std::cout << "tr: " << what[pos] << "  " << chars[newIdx-1] << std::endl;
				std::cout << chars[newIdx-1] << " != " << check[pos] << std::endl;
				enc.push_back(chars[ newIdx-1 ]);

// 				assert(chars[newIdx-1] == check[pos] );
			} else {
				enc.push_back(what[pos]);
			}

		}
		return enc;
	}
};

struct Decoder {

	static std::string decode (const std::string& what, const std::string & check = "") {
		std::string const chars{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,? "};
		std::string res; res.reserve(what.size());
		for( size_t pos = 0; pos < what.size(); ++pos ){
			if( chars.find( what[pos] ) != std::string::npos ){
				size_t idx = chars.find(what[pos]) + 1;
				for( size_t i = 0; i <= pos; ++i )
				{
					while( idx % 2 == 1 || idx == 0 ){
						idx += 67;
					}
					idx /= 2;
				}

				res.push_back(chars[idx-1]);
			} else {
				res.push_back(what[pos]);
			}
		}

		return res;
	}

	static std::string encode (std::string const & what, std::string const & check){
		std::string const chars{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,? "};
		for( auto i = 0; i < chars.size() ; ++ i ){
			std::cout << i + 1 << " = " << chars[i] << std::endl;
		}

		std::string enc;
		for( size_t pos = 0; pos < what.size(); ++pos ){
			if( chars.find( what[pos] ) != std::string::npos ){
				uint64_t idx = chars.find(what[pos]) + 1;

				//uint64_t idx =  ( ( (uint64_t)2 << pos ) * idx  ) % ( chars.size() + 1 );

				uint64_t newIdx = 2 * idx;
				for( auto i = 0; i < pos; ++i ){
					newIdx *= 2;
					while(newIdx > 67 )
						newIdx -= 67;
				}
					while(newIdx > 67 )
						newIdx -= 67;

				//assert(newIdx == idx);

				std::cout << "tr: " << what[pos] << "  " << chars[newIdx-1] << std::endl;
				std::cout << chars[newIdx-1] << " != " << check[pos] << std::endl;
				enc.push_back(chars[ newIdx-1 ]);

				assert(chars[newIdx-1] == check[pos] );
			} else {
				enc.push_back(what[pos]);
			}
		}
		return enc;
	}
};
